import Watcher from './watcher.js'

export default class Compiler {
  constructor (vm) {
    // Vue 组件实例
    this.vm = vm
    this.el = vm.$el

    this.compile(this.el)
  }

  // 编译 el
  compile (el) {
    const childNodes = el.childNodes
    childNodes.forEach(node => {
      if (this.isTextNode(node)) {
        this.compileTextNode(node)
      } else if (this.isElementNode(node)) {
        this.compileElementNode(node)
      }

      // 如果当前 node 有子节点，则递归编译子节点
      if (node.childNodes?.length) {
        this.compile(node)
      }
    })
  }

  // 编译文本节点：{{ msg }}
  compileTextNode (node) {
    const reg = /\{\{(.+?)\}\}/
    let text = node.textContent
    if (reg.test(text)) {
      const key = text.match(reg)[1].trim()

      node.textContent = text.replace(reg, this.vm[key])

      new Watcher(this.vm, key, newValue => {
        node.textContent = text.replace(reg, newValue)
      })
    }
  }

  // 编译 Element 节点
  compileElementNode (node) {
    const attrs = node.attributes
    for (let attr of attrs) {
      const attrName = attr.name
      const key = attr.value

      if (attrName === 'v-text') this.textCompile(node, key)
      if (attrName === 'v-html') this.htmlCompile(node, key)
      if (attrName === 'v-model') this.modelCompile(node, key)

      // 匹配 v-on:click / @click 语法
      const reg = /(v-on:|@)(\w+)/
      if (reg.test(attrName)) {
        const event = attrName.match(reg)[2]
        this.onCompile(node, key, event)
      }
    }
  }

  // v-text 语法编译
  textCompile (node, key) {
    node.textContent = this.vm[key]

    new Watcher(this.vm, key, newValue => {
      node.textContent = newValue
    })
  }

  // v-html 语法编译
  htmlCompile (node, key) {
    node.innerHTML = this.vm[key]

    new Watcher(this.vm, key, newValue => {
      node.innerHTML = newValue
    })
  }

  // v-model 语法编译
  modelCompile (node, key) {
    node.value = this.vm[key]

    // 输入改变数据改变
    node.addEventListener('input', () => {
      this.vm[key] = node.value
    })

    new Watcher(this.vm, key, newValue => {
      node.value = newValue
    })
  }

  // v-on @ 绑定事件
  onCompile (node, key, event) {
    node.addEventListener(event, () => {
      // 将处理函数绑定到 Vue 实例上执行
      this.vm[key].call(this.vm)
    })
  }

  // 是否是 element 节点
  isElementNode (node) {
    return node.nodeType === 1
  }

  // 是否是 text 节点
  isTextNode (node) {
    return node.nodeType === 3
  }
}
